package com.fastloan.mgt.daily.service.impl;

import com.fastloan.common.BasicServiceImpl;
import com.fastloan.common.model.CheckResult;
import com.fastloan.mgt.daily.dao.WCustomerManagerDailyCommitMapper;
import com.fastloan.mgt.daily.dao.WCustomerManagerDailyMapper;
import com.fastloan.mgt.daily.enums.WxDailyCommitType;
import com.fastloan.mgt.daily.model.WCustomerManagerDaily;
import com.fastloan.mgt.daily.model.WCustomerManagerDailyCommit;
import com.fastloan.mgt.daily.model.vo.CommitUser;
import com.fastloan.mgt.daily.model.vo.WCustomerManagerDailyExcelVo;
import com.fastloan.mgt.daily.model.vo.WCustomerManagerDailyVo;
import com.fastloan.mgt.daily.service.WCustomerManagerDailyService;
import com.fastloan.safe.core.param.RoleTypeEnum;
import com.fastloan.safe.core.util.ManagerKit;
import com.fastloan.safe.mgt.auth.model.CmuRole;
import com.fastloan.safe.mgt.auth.model.CmuUserRole;
import com.fastloan.safe.mgt.auth.service.CmuRoleService;
import com.fastloan.safe.mgt.auth.service.CmuUserRoleService;
import com.fastloan.safe.mgt.org.model.CmuOrg;
import com.fastloan.safe.mgt.org.model.CmuOrgUser;
import com.fastloan.safe.mgt.org.service.CmuOrgService;
import com.fastloan.safe.mgt.org.service.CmuOrgUserService;
import com.fastloan.safe.mgt.user.model.CmuUser;
import com.fastloan.safe.mgt.user.service.CmuUserService;
import com.fastloan.utils.DateTimeUtil;
import com.fastloan.utils.DozerUtil;
import com.fastloan.utils.excelutil.DateCheckUtil;
import com.fastloan.utils.excelutil.ExcelUtil;
import com.fastloan.utils.excelutil.exception.ExpertExcelException;
import com.fastloan.utils.excelutil.model.BetweenDate;
import com.kawins.result.ResultEnum;
import com.kawins.result.ResultTo;
import com.kawins.supers.table.manager.DataTable;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.Example;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;

/**
 * @author yanwe
 *         createTime 2017-08-2017/8/16 10:47
 */
@Service
public class WCustomerManagerDailyServiceImpl extends BasicServiceImpl<WCustomerManagerDaily> implements WCustomerManagerDailyService {

    @Autowired
    private WCustomerManagerDailyMapper customerManagerDailyMapper;

    @Autowired
    private WCustomerManagerDailyCommitMapper customerManagerDailyCommitMapper;

    @Autowired
    private CmuOrgUserService userOrganizationService;

    @Autowired
    private CmuOrgService organizationService;

    @Autowired
    private CmuUserService userService;

    @Autowired
    private CmuRoleService roleService;

    @Autowired
    private CmuUserRoleService userRoleService;

    @Override
    public boolean commitDaily(Integer dailyCommitId, String remark){
        WCustomerManagerDaily customerManagerDaily = new WCustomerManagerDaily();
        customerManagerDaily.setDailyCommitId(dailyCommitId);
        int count = customerManagerDailyMapper.selectCount(customerManagerDaily);
        if (count == 0){
            return false;//没有日报不能提交
        }
        WCustomerManagerDailyCommit customerManagerDailyCommit = new WCustomerManagerDailyCommit();
        customerManagerDailyCommit.setDailyCommitId(dailyCommitId);
        customerManagerDailyCommit.setRemark(remark);
        customerManagerDailyCommit.setCommitTime(new Date());
        customerManagerDailyCommit.setDailyCommitStatus(WxDailyCommitType.IS_COMMIT.getType());
        int result = customerManagerDailyCommitMapper.updateByPrimaryKeySelective(customerManagerDailyCommit);
        return result == 1;
    }

    @Override
    public void outPutExcel(HttpServletRequest request, HttpServletResponse response) throws ExpertExcelException {
        List<WCustomerManagerDailyExcelVo> customerManagerDailyExcelVos = selectDaily(buildParameter(request));
        HSSFWorkbook workbook = convert2Excel(customerManagerDailyExcelVos);
        ExcelUtil.outPut(response,"客户经理日报",workbook);
    }

    @Override
    public ResultTo selectDailyStatistics(){
        Map<String,Object> map = new HashMap<>();
        List<String> orgIds = getChildOrgIds(ManagerKit.getTopOrgId());
        map.put("orgIds",orgIds);
        String now = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
        map.put("beginTime",now);
        map.put("endTime",now);
        List<WCustomerManagerDailyCommit> customerManagerDailyCommits = selectCustomerManagerDailyList(map);
        long isCommitCount = customerManagerDailyCommits.stream()
                .filter(commit -> WxDailyCommitType.IS_COMMIT.getType()
                .equals(commit.getDailyCommitStatus())).count();
        CmuRole cmuRole = new CmuRole();
        cmuRole.setTopOrgId(ManagerKit.getTopOrgId());
        cmuRole.setIndexType(RoleTypeEnum.MANAGER.getCode());
        CmuRole manager = roleService.selectOne(cmuRole);
        CmuUserRole cmuUserRole = new CmuUserRole();
        cmuUserRole.setRoleId(manager.getId());
        long customerManagerCount = userRoleService.select(cmuUserRole).size();
        return new ResultTo().setData("isCommitCount",isCommitCount)
                .setData("notCommitCount",customerManagerCount - isCommitCount);
    }

    private List<String> getChildOrgIds(String orgId) {
        return organizationService.selectTopOrgBySon(orgId,true,false).stream()
                    .map(CmuOrg::getId).collect(toList());
    }

    @Override
    @DataTable(name = "selectCustomerManagerDailyList")
    public List<WCustomerManagerDailyCommit> selectCustomerManagerDailyList(Map<String, Object> map){
        return customerManagerDailyCommitMapper.selectCustomerManagerDailyList(map);
    }

    @Override
    public WCustomerManagerDailyVo getDaily(String customerManagerId, LocalDateTime dateTime){
        Optional<WCustomerManagerDailyCommit> customerManagerDailyCommitOptional =
                getCustomerManagerDailyCommit(customerManagerId, dateTime);
        if (!customerManagerDailyCommitOptional.isPresent()){
            return null;
        }
        WCustomerManagerDailyCommit customerManagerDailyCommit = customerManagerDailyCommitOptional.get();
        return getCustomerManagerDailyVo(customerManagerId, customerManagerDailyCommit);
    }

    @Override
    public WCustomerManagerDailyVo getDaily(Integer dailyCommitId){
        WCustomerManagerDailyCommit customerManagerDailyCommit = customerManagerDailyCommitMapper
                .selectByPrimaryKey(dailyCommitId);
        if (customerManagerDailyCommit == null){
            return null;
        }
        return getCustomerManagerDailyVo(customerManagerDailyCommit.getCustomerManagerId(), customerManagerDailyCommit);
    }

    @Override
    @Transactional
    public ResultTo addDaily(WCustomerManagerDaily customerManagerDaily){
        if (customerManagerDaily == null){
            return new ResultTo(ResultEnum.OPERATION_FAILED);
        }
        LocalDateTime now = LocalDateTime.now();
        CheckResult checkResult = checkDaily(customerManagerDaily,now, ManagerKit.getUserId());
        if (!checkResult.isCheckResult()){
            return new ResultTo(ResultEnum.OPERATION_FAILED,checkResult.getCheckMsg());
        }
        customerManagerDaily.setCreateTime(DateTimeUtil.localDate2DateTime(now));
        updateCommit(customerManagerDaily, now);//更新总工时
        return customerManagerDailyMapper.insert(customerManagerDaily) == 1 ?
                new ResultTo() : new ResultTo(ResultEnum.OPERATION_FAILED);
    }

    @Override
    public ResultTo updateDaily(WCustomerManagerDaily customerManagerDaily){
        if (customerManagerDaily == null){
            return new ResultTo(ResultEnum.OPERATION_FAILED);
        }
        LocalDateTime now = LocalDateTime.now();
        CheckResult checkResult = checkDaily(customerManagerDaily,now,ManagerKit.getUserId());
        if (!checkResult.isCheckResult()){
            return new ResultTo(ResultEnum.OPERATION_FAILED,checkResult.getCheckMsg());
        }
        int result = customerManagerDailyMapper.updateByPrimaryKeySelective(customerManagerDaily);
        updateCommit(customerManagerDaily, now);
        return result == 1 ?
                new ResultTo() : new ResultTo(ResultEnum.OPERATION_FAILED);
    }

    @Override
    @Transactional
    public ResultTo deleteDaily(Integer dailyId){
        WCustomerManagerDaily customerManagerDaily = customerManagerDailyMapper.selectByPrimaryKey(dailyId);
        if (customerManagerDaily == null){
            return new ResultTo(ResultEnum.OPERATION_FAILED,"该日报不存在无法删除！");
        }
        customerManagerDailyMapper.deleteByPrimaryKey(dailyId);
        WCustomerManagerDailyCommit customerManagerDailyCommit = customerManagerDailyCommitMapper
                .selectByPrimaryKey(customerManagerDaily.getDailyCommitId());
        customerManagerDailyCommit.setSumWorkTime(customerManagerDailyCommit.getSumWorkTime()
                - customerManagerDaily.getWorkTime());
        int result = customerManagerDailyCommitMapper.updateByPrimaryKeySelective(customerManagerDailyCommit);
        return result == 1 ?
                new ResultTo() : new ResultTo(ResultEnum.OPERATION_FAILED);
    }

    @Override
    public boolean checkCommit(String customerManagerId){
        Optional<WCustomerManagerDailyCommit> customerManagerDailyCommit =
                getCustomerManagerDailyCommit(customerManagerId, LocalDateTime.now());
        return customerManagerDailyCommit.map(wCustomerManagerDailyCommit ->
                !wCustomerManagerDailyCommit.getDailyCommitStatus()
                        .equals(WxDailyCommitType.IS_COMMIT.getType())).orElse(true);
    }

    @Override
    @Scheduled(cron = "0 00 1 * * ?")
    public synchronized void batchInsertDailyCommit(){
        List<CommitUser> userList = customerManagerDailyCommitMapper
                .selectCommitUser(Collections.singletonList(RoleTypeEnum.MANAGER.getCode())).stream()
                .filter(commitUser -> commitUser.getIsHave() == null)
                .collect(toList());
        List<WCustomerManagerDailyCommit> customerManagerDailyCommits = userList.stream()
                .map(user -> buildCustomerManagerDailyCommit(user.getUserId(), new Date(), 0.0))
                .collect(Collectors.toList());
        if(!customerManagerDailyCommits.isEmpty()){
            customerManagerDailyCommits.forEach(customerManagerDailyCommitMapper::insert);
        }
    }

    @Override
    public boolean addCustomerManagerDailyCommit(String userId) {
        Optional<WCustomerManagerDailyCommit> customerManagerDailyCommit =
                getCustomerManagerDailyCommit(userId, LocalDateTime.now());
        return !customerManagerDailyCommit.isPresent() && customerManagerDailyCommitMapper
                .insert(buildCustomerManagerDailyCommit(userId, new Date(), 0.0)) == 1;
    }

    private WCustomerManagerDailyCommit buildCustomerManagerDailyCommit(String userId
            , Date createTime, double sumWorkTime) {
        WCustomerManagerDailyCommit customerManagerDailyCommit = new WCustomerManagerDailyCommit();
        customerManagerDailyCommit.setCustomerManagerId(userId);
        customerManagerDailyCommit.setCreateTime(createTime);
        customerManagerDailyCommit.setDailyCommitStatus(WxDailyCommitType.NOT_COMMIT.getType());
        customerManagerDailyCommit.setSumWorkTime(sumWorkTime);
        return customerManagerDailyCommit;
    }

    private void updateCommit(WCustomerManagerDaily newDaily, LocalDateTime now) {
        Optional<WCustomerManagerDailyCommit> customerManagerDailyCommitOptional =
                getCustomerManagerDailyCommit(ManagerKit.getUserId(),now);
        newDaily.setWorkTime(getWorkTime(newDaily.getBeginTime(),newDaily.getEndTime()));//设置工时
        if (customerManagerDailyCommitOptional.isPresent()){
            WCustomerManagerDailyCommit customerManagerDailyCommit = customerManagerDailyCommitOptional.get();
            //设置总工时
            if (newDaily.getDailyId() == null){//新增日报时
                customerManagerDailyCommit.setSumWorkTime(customerManagerDailyCommit.getSumWorkTime()
                        + newDaily.getWorkTime());
                newDaily.setDailyCommitId(customerManagerDailyCommit.getDailyCommitId());
            } else {//更新日报时
                WCustomerManagerDaily oldDaily =
                        customerManagerDailyMapper.selectByPrimaryKey(newDaily.getDailyId());
                customerManagerDailyCommit.setSumWorkTime(customerManagerDailyCommit.getSumWorkTime()
                        - oldDaily.getWorkTime() + newDaily.getWorkTime());
            }
            customerManagerDailyCommitMapper.updateByPrimaryKeySelective(customerManagerDailyCommit);
        } else {
            WCustomerManagerDailyCommit customerManagerDailyCommit = buildCustomerManagerDailyCommit(
                    ManagerKit.getUserId(), DateTimeUtil.localDate2DateTime(now), newDaily.getWorkTime());
            customerManagerDailyCommitMapper.insert(customerManagerDailyCommit);
            newDaily.setDailyCommitId(customerManagerDailyCommit.getDailyCommitId());
        }
    }

    private CheckResult checkDaily(WCustomerManagerDaily customerManagerDaily,LocalDateTime dateTime
            ,String customerManagerId) {
        WCustomerManagerDailyCommit customerManagerDailyCommit =
                customerManagerDailyCommitMapper.selectByPrimaryKey(customerManagerDaily.getDailyCommitId());
        if (customerManagerDailyCommit != null &&
                customerManagerDailyCommit.getDailyCommitStatus().equals(WxDailyCommitType.IS_COMMIT.getType())){
            return new CheckResult("已提交的日报不能再次修改或添加！");
        }
        LocalDateTime beginTime = DateTimeUtil.date2LocalDateTime(customerManagerDaily.getBeginTime());
        LocalDateTime endTime = DateTimeUtil.date2LocalDateTime(customerManagerDaily.getEndTime());
        if (endTime.isBefore(beginTime) || endTime.isEqual(beginTime)
                || endTime.getDayOfYear() - beginTime.getDayOfYear() != 0){
            return new CheckResult("起止时间有误");
        }
        List<WCustomerManagerDaily> customerManagerDailyList = getCustomerManagerDaily(customerManagerId,dateTime);
        if (customerManagerDailyList.isEmpty()){
            return new CheckResult();
        }
        if (customerManagerDaily.getDailyId() != null){
            customerManagerDailyList
                    .removeIf(daily -> customerManagerDaily.getDailyId().equals(daily.getDailyId()));
        }
        List<BetweenDate> betweenDates = DateCheckUtil.convert2BetweenDate(customerManagerDailyList
                , WCustomerManagerDaily::getBeginTime, WCustomerManagerDaily::getEndTime);
        betweenDates.add(new BetweenDate(beginTime,endTime));
        return DateCheckUtil.checkDateCross(betweenDates) ? new CheckResult("时间交叉") : new CheckResult();
    }

    private double getWorkTime(Date beginTime,Date endTime){
        Instant beginInstant = beginTime.toInstant();
        Instant endInstant = endTime.toInstant();
        Duration duration = Duration.between(beginInstant, endInstant);
        long seconds = duration.getSeconds();
        return BigDecimal.valueOf(seconds / 3600d).setScale(1
                , BigDecimal.ROUND_DOWN).doubleValue();
    }

    private Optional<WCustomerManagerDailyCommit> getCustomerManagerDailyCommit(String customerManagerId
            , LocalDateTime dateTime) {
        Example example = new Example(WCustomerManagerDailyCommit.class);
        example.createCriteria().andEqualTo("customerManagerId",customerManagerId)
                .andEqualTo("createTime",dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE));
        List<WCustomerManagerDailyCommit> customerManagerDailyCommits =
                customerManagerDailyCommitMapper.selectByExample(example);
        if (customerManagerDailyCommits.isEmpty()){
            return Optional.empty();
        } else {
            if (customerManagerDailyCommits.size() > 1){
                String errorMsg = "客户经理id:"+ManagerKit.getUserId()
                        +",日期:"+dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE)+",日报数据重复";
                log.warn("日报提交数据错误",errorMsg);
                deleteErrorData(dateTime);//删除错误数据
                log.warn("日报错误数据处理","错误数据清理完毕");
                getCustomerManagerDailyCommit(customerManagerId,dateTime);//重新获取数据
            }
            return Optional.ofNullable(customerManagerDailyCommits.get(0));
        }
    }

    private void deleteErrorData(LocalDateTime dateTime) {
        Example example = new Example(WCustomerManagerDailyCommit.class);
        example.createCriteria()
                .andEqualTo("createTime",dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE));
        List<WCustomerManagerDailyCommit> customerManagerDailyCommits =
                customerManagerDailyCommitMapper.selectByExample(example);
        customerManagerDailyCommits.stream()
                .collect(groupingBy(WCustomerManagerDailyCommit::getCustomerManagerId))//根据客户经理分组
                .entrySet().stream()
                .filter(entry -> entry.getValue().size() > 1)//找出有重复数据的客户经理
                .forEach(entry -> {
                    int minCommitId = entry.getValue().stream().mapToInt(WCustomerManagerDailyCommit::getDailyCommitId)
                            .min().orElse(0);//找出最小id
                    entry.getValue().stream()//删除重复数据
                            .filter(commit -> !commit.getDailyCommitId().equals(minCommitId))
                            .forEach(commit -> customerManagerDailyCommitMapper
                                    .deleteByPrimaryKey(commit.getDailyCommitId()));
                });
    }

    private List<WCustomerManagerDaily> getCustomerManagerDaily(String customerManagerId,LocalDateTime dateTime){
        Optional<WCustomerManagerDailyCommit> customerManagerDailyCommit =
                getCustomerManagerDailyCommit(customerManagerId, dateTime);
        if (customerManagerDailyCommit.isPresent()){
            Example example = new Example(WCustomerManagerDaily.class);
            example.createCriteria().andEqualTo("dailyCommitId"
                    ,customerManagerDailyCommit.get().getDailyCommitId());
            return customerManagerDailyMapper.selectByExample(example);
        } else {
            return new ArrayList<>();
        }
    }

    private List<WCustomerManagerDaily> getCustomerManagerDaily(Integer dailyCommitId){
        Example example = new Example(WCustomerManagerDaily.class);
        example.createCriteria().andEqualTo("dailyCommitId",dailyCommitId);
        return customerManagerDailyMapper.selectByExample(example);
    }

    private WCustomerManagerDailyVo getCustomerManagerDailyVo(String customerManagerId
            , WCustomerManagerDailyCommit customerManagerDailyCommit) {
        List<WCustomerManagerDaily> customerManagerDailyList =
                getCustomerManagerDaily(customerManagerDailyCommit.getDailyCommitId());
        customerManagerDailyList.sort(Comparator.comparing(WCustomerManagerDaily::getBeginTime));
        WCustomerManagerDailyVo customerManagerDailyVo = new WCustomerManagerDailyVo();
        customerManagerDailyVo.setCustomerManagerDailyList(customerManagerDailyList);
        customerManagerDailyVo.setCustomerManagerName(userService.selectByPrimaryKey(customerManagerId).getCname());
        customerManagerDailyVo.setCustomerManagerId(customerManagerId);
        customerManagerDailyVo.setRemark(customerManagerDailyCommit.getRemark());
        customerManagerDailyVo.setDailyCommitId(customerManagerDailyCommit.getDailyCommitId());
        customerManagerDailyVo.setDailyCommitStatus(customerManagerDailyCommit.getDailyCommitStatus());
        CmuOrg org = getOrgByUserId(customerManagerId);
        customerManagerDailyVo.setOrgId(org.getId());
        customerManagerDailyVo.setOrgName(org.getName());
        return customerManagerDailyVo;
    }

    private CmuOrg getOrgByUserId(String userId){
        CmuOrgUser cmuOrg = new CmuOrgUser();
        cmuOrg.setUserId(userId);
        CmuOrgUser cmuOrgUser = userOrganizationService.selectOne(cmuOrg);
        return organizationService.selectByPrimaryKey(cmuOrgUser.getOrgId());
    }

    private List<WCustomerManagerDailyExcelVo> selectDaily(Map<String, Object> map) {
        return selectCustomerManagerDailyList(map).stream()
                .flatMap(commit -> {
                    String customerManagerId = commit.getCustomerManagerId();
                    CmuOrg organization = getOrgByUserId(customerManagerId);
                    CmuUser user = userService.selectByPrimaryKey(customerManagerId);
                    return getCustomerManagerDaily(commit.getDailyCommitId()).stream()
                            .map(customerManagerDaily -> DozerUtil.map(customerManagerDaily, WCustomerManagerDailyExcelVo.class)
                                    .setUserCname(user.getCname())
                                    .setUserName(user.getUsername())
                                    .setOrgName(organization.getName())
                                    .setDate(commit.getCommitTime())
                                    .setExperience(commit.getRemark()));
                }).collect(Collectors.toList());
    }

    private Map<String, Object> buildParameter(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String,Object> map = parameterMap.entrySet().stream()
                .filter(entry -> !StringUtils.isEmpty(entry.getValue()[0]))
                .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue()[0]));
        String orgId = map.get("orgId").toString();
        map.put("orgIds",getChildOrgIds(orgId));
        map.put("dailyCommitStatus",WxDailyCommitType.IS_COMMIT.getType());
        return map;
    }

    private HSSFWorkbook convert2Excel(List<WCustomerManagerDailyExcelVo> customerManagerDailyExcelVos) {
        HSSFWorkbook workbook = new HSSFWorkbook();
        if (customerManagerDailyExcelVos.isEmpty()){
            return workbook;
        }
        try {
            ExcelUtil.createTableSheet(workbook,customerManagerDailyExcelVos
                    ,"客户经理日报","客户经理日报");
        } catch (ExpertExcelException e) {
            e.printStackTrace();
            log.error("excel创建错误","微信客户经理日报创建错误！");
        }
        HSSFSheet sheet = workbook.getSheetAt(0);
        int cellSize = sheet.getLastRowNum();
        List<HSSFRow> rowList = IntStream.rangeClosed(2, cellSize).mapToObj(sheet::getRow)
                .collect(Collectors.toList());
        rowList.stream().collect(groupingBy(row -> row.getCell(3).getStringCellValue()//先按日期分组
                        ,groupingBy(row -> row.getCell(2).getStringCellValue())))//然后按账号分组
                .entrySet().stream()
                .map(Map.Entry::getValue)
                .forEach(map -> map.entrySet().stream()
                                .map(Map.Entry::getValue)
                                .filter(v -> v.size() > 1)
                                .forEach(v -> sheet.addMergedRegion(new CellRangeAddress(v.get(0).getRowNum()
                                        , v.get(v.size() - 1).getRowNum(), v.get(0).getLastCellNum() - 1
                                        ,v.get(0).getLastCellNum() - 1))));
        return workbook;
    }
}
